package com.company;

//Java多线程系列--“基础篇”11之 生产消费者问题
//https://www.cnblogs.com/skywang12345/p/3480016.html
/*
(01) Producer是“生产者”类，它与“仓库(depot)”关联。当调用“生产者”的produce()方法时，它会新建一个线程并向“仓库”中生产产品。
(02) Customer是“消费者”类，它与“仓库(depot)”关联。当调用“消费者”的consume()方法时，它会新建一个线程并消费“仓库”中的产品。
(03) Depot是“仓库”类，仓库中记录“仓库的容量(capacity)”以及“仓库中当前产品数目(size)”。
“仓库”类的生产方法produce()和消费方法consume()方法都是synchronized方法，进入synchronized方法体，
意味着这个线程获取到了该“仓库”对象的同步锁。
这也就是说，同一时间，生产者和消费者线程只能有一个能运行。通过同步锁，实现了对“残酷”的互斥访问。
对于生产方法produce()而言：当仓库满时，生产者线程等待，需要等待消费者消费产品之后，生产线程才能生产；
生产者线程生产完产品之后，会通过notifyAll()唤醒同步锁上的所有线程，包括“消费者线程”，即我们所说的“通知消费者进行消费”。
对于消费方法consume()而言：当仓库为空时，消费者线程等待，需要等待生产者生产产品之后，消费者线程才能消费；
消费者线程消费完产品之后，会通过notifyAll()唤醒同步锁上的所有线程，包括“生产者线程”，即我们所说的“通知生产者进行生产”。
*/

public class C07MultiThreadingWaitNotify_sample {


    public static void main(String[] args) throws InterruptedException {
        Cangku cangku = new Cangku(150, 10);

        Shengchanzhe shengchanzhe = new Shengchanzhe(cangku);
        Xiaofeizhe xiaofeizhe = new Xiaofeizhe(cangku);
        xiaofeizhe.xiaoFei(60);
        xiaofeizhe.xiaoFei(60);
        xiaofeizhe.xiaoFei(60);
        xiaofeizhe.xiaoFei(60);
        xiaofeizhe.xiaoFei(60);
        xiaofeizhe.xiaoFei(60);
        shengchanzhe.shengChan(10);
        shengchanzhe.shengChan(10);
        shengchanzhe.shengChan(10);
        shengchanzhe.shengChan(10);
        shengchanzhe.shengChan(10);
        shengchanzhe.shengChan(10);
        shengchanzhe.shengChan(10);
        shengchanzhe.shengChan(10);
    }

    // 仓库
    public static class Cangku {
        public Integer 最大库存 = 100; // 最大库存

        public volatile Integer 实际库存 = 100; // 实际库存

        public Cangku(Integer maXkucun, Integer kucun) {
            this.最大库存 = maXkucun;
            this.实际库存 = kucun;
        }


        public synchronized void shengChan(Integer 想要生产数) {
            Integer 想要生产数0=想要生产数;
            Integer 剩余生产数=想要生产数;
            Integer 生产次数=0;
            Integer 已生产数=0;
            try {
//            想要生产数 如果大于最大库存数 就要多次生产
                // 两次循环的解释
                // 【while (剩余生产数 > 0)】
                //      假如要生产100件，库存是50件 ，但是最大库存是100件
                //      所以就要分多次生产 。
                // 【while (实际库存 >= 最大库存)】：
                //      多次生产 无论那一次都要去看下库存是不是已经满了。如果已经满了就处于等待，并且释放锁
                //      当被重新唤醒后，还要去看下库存是不是已经满了如果满了就继续等待，如果没有满就生产。

                // 生产必须看库存，量超过库存，剩下的就下下次循环（剩余生产数）
                while (剩余生产数 > 0) {
                    while (实际库存 >= 最大库存) {
//                        System.out.println(Thread.currentThread().getName()+"******生产等待中******"+
//                                "想要生产数:"+想要生产数0.toString()+";本次实际生产数:0;实际库存:"+实际库存.toString() +"；最大库存:"+最大库存.toString());
                        this.wait();
                    }
                 
                    //  得到能生产的数量
                    Integer 本次实际生产数 = 剩余生产数 <= (最大库存 - 实际库存) ? 剩余生产数 : (最大库存 - 实际库存);
                    已生产数=已生产数+本次实际生产数;
                    生产次数++;
                    实际库存 += 本次实际生产数;
                    剩余生产数 -= 本次实际生产数;
                    System.out.println(Thread.currentThread().getName()+"--"+
                            "想要生产数:"+想要生产数0.toString()+";已生产数:"+已生产数.toString()+"生产次数:第"+生产次数.toString()+
                            "次;本次实际生产数:"+本次实际生产数.toString()+";实际库存:"+实际库存.toString()+"；最大库存:"+最大库存.toString());
                    this.notifyAll();
                }

            } catch (InterruptedException ex) {
                System.out.println(ex);
            }
        }

        public synchronized void xiaoFei(Integer 想要消费数) {
            Integer 想要消费数0=想要消费数;
            Integer 剩余消费数=想要消费数;
            Integer 消费次数=0;
            Integer 已消费数=0;
            try {
                // 两次循环的解释
                // 【while (剩余消费数 > 0)】
                //      假如要消费100件，库存是50件
                //      所以就要分多次消费 。
                // 【 while (实际库存 <= 0)】：
                //      多次消费 无论那一次都要去看下库存是不是0。如果已经是0就处于等待，并且释放锁
                //      当被重新唤醒后，还要去看下库存是不是0，如果是0就继续等待，如果>就消费。

                // 生产必须看库存，量超过库存，剩下的就下下次循环（剩余生产数）
                while (剩余消费数 > 0) {
                    while (实际库存 <= 0) {
//                        System.out.println(Thread.currentThread().getName()+"######消费等待中######"+
//                                "想要消费数:"+想要消费数0.toString()+";本次消费数:0;实际库存:"+实际库存.toString()+"；最大库存:"+最大库存.toString());
                        wait();
                    }
                    //  得到能消费数
                    Integer 本次消费数 = 实际库存>=剩余消费数? 剩余消费数 : 实际库存;
                    消费次数++;
                    已消费数=已消费数+本次消费数;
                    剩余消费数-=本次消费数;
                    实际库存-=本次消费数;
                    System.out.println(Thread.currentThread().getName()+"--"+
                            "想要消费数:"+想要消费数0.toString()+";本次消费数:"+本次消费数.toString()+";已消费数:"+已消费数.toString()
                            +";消费次数:"+消费次数.toString()+";实际库存:"+实际库存.toString()+"；最大库存:"+最大库存.toString());
                    this.notifyAll();
                }
            } catch (InterruptedException ex) {
                System.out.println(ex);
            }
        }
    }

    // 生产者
    public static class Shengchanzhe {
        Cangku cangku;

        public Shengchanzhe(Cangku cangku) {
            this.cangku = cangku;
        }

        // 生产者 生产
        public void shengChan(Integer xiangyaoShengchanShu) {
            // 生产者 生产线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    cangku.shengChan(xiangyaoShengchanShu);
                }
            }).start();
        }
    }

    // 消费者
    public static class Xiaofeizhe {

        Cangku cangku;

        public Xiaofeizhe(Cangku cangku) {
            this.cangku = cangku;
        }

        // 消费者 消费
        public void xiaoFei(Integer xiangyaoXiaofeiShu) {
            // 消费者 消费线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    cangku.xiaoFei(xiangyaoXiaofeiShu);
                }
            }).start();
        }
    }
}
